
var OTHERJSONRPCClient = function (opts) {
    this.options = @.merge.advanced({
        "!valueType": "object",
        "version" : {
            "!valueType": "string",
            "!defaultValue": "2.0"
        },
        "rootURL": {
            "!valueType": "string",
            "!defaultValue": ""
        },
        "timeout": {
            "!valueType": "number",
            "!defaultValue": 1000000
        },
        "gatewayPath" : {
            "!defaultValue":  "/gateway/api/jsonrpc.jsp",
            "!valueType": "string"
        },
        "accessToken": {
            "!defaultValue": "",
            "!valueType": "string"
        },
        "appID" : {
            "!valueType": "string"
        },
        "appSecret" : {
            "!valueType": "string"
        },
        "acquireToken" : {
            "!valueType": "function"
        },
        "mewSID": {
            "!defaultValue": "",
            "!valueType": "string"
        },
    }, opts);

    if (["1.0" , "2.0"].indexOf(this.options.version) < 0) {
        throw new Error("version not support : " + this.options.version);
    }

    this.routeTable = {};

    if (this.options.rootURL) {

        this.routeTable['*'] = {
            "rootURL" : this.options.rootURL,
            "client"  : @.net.httpClient(),
            "appID"   : this.options.appID,
            "appSecret"   : this.options.appSecret,
        };

        if (this.options.mewSID) {
            this.routeTable['*'].client.cookies["MewSID"] = this.options.mewSID;
        }

    } else {
        throw new Error("root URL not defined");
    }
};

var directRPC = function(client, path, parameters, callback, route ){

    if (!route) {
        if (client.routeTable[path]) {
            route = client.routeTable[path];
        } else if (client.routeTable[path.split(".")[0]]) {
            route = client.routeTable[path.split(".")[0]];
        } else {
            route = client.routeTable["*"];
        }
    }

    var replyCallback = function (err, res) {
        try {
            if (callback) {
                callback(err, res, route);
            }
        } catch (ex) {
            @error(ex);
        }
    };

    var payload = JSON.stringify({
        "jsonrpc": client.options.version || "2.0",
        "method" : path,
        "params" : parameters,
        "id"     : Date.now()
    });

    var body;
    var headers = {};

    switch (client.options.version){
        case "2.0" : {
            body = payload;
            break;
        }
        case "1.0" : {
            body = {
                query : payload
            };
            if (route.appID && route.appSecret) {
                headers["other-app-id"]     = route.appID;
                headers["other-app-secret"] = route.appSecret;
            }
            break;
        }
    }
    
    route.client.request(route.rootURL + client.options.gatewayPath, {
        "method"  : "post",
        "data"    : body,
        "headers" : headers,
        "onError" : function (error) {
            replyCallback(error);
        },
        "timeout" : client.options.timeout,
        "onSuccess": function (response, httpResponse) {
            if (response) {
                var response = JSON.parse(response.toString("utf-8"));
                if (response.error) {
                    replyCallback(response.error);
                } else {
                    replyCallback(null, response.result);
                }
            } else {
                replyCallback("response not found");
            }
        }
    });
}


OTHERJSONRPCClient.prototype.defineRoute = function (domains, rootURL) {

    if (domains && rootURL) {

        domains.split(",").forEach(function (domain) {
            this.routeTable[domain] = {
                "rootURL": rootURL,
                "client": @.net.httpClient()
            };
        }.bind(this));

    } else {
        throw new Error("domain or root url not defined");
    }
};

OTHERJSONRPCClient.prototype.call = function(path, parameters, callback) {

    var client = this;

    if (@.is(parameters,Function)) {
        callback   = parameters;
        parameters = [];
    } else if (!Array.isArray(parameters)){
        parameters = [parameters];
    }

    directRPC(client,path,parameters,function(error,result,route){
        if (error) {
            if (error.code && error.code == 592) {

                if (client.options.accessToken) {
                    directRPC(client,"backend@keys.participant",[client.options.accessToken], function(error,result){
                        if (error) {
                            callback && callback(error);
                        } else {
                            directRPC(client,path,parameters,function(error,result){
                                callback && callback(error,result);
                            })
                        }
                    })
                } else if (client.options.acquireToken) {
                    try {
                        @.async.ensure(client.options.acquireToken(client)).finished(function (error) {
                            if (error) {
                                callback && callback(error);
                            } else {
                                directRPC(client,path,parameters,function(error,result){
                                    callback && callback(error,result);
                                })
                            }
                        })
                    } catch (ex) {
                        callback && callback(error);
                    }
                    
                } else {
                    callback && callback(error);
                }
            } else {
                callback && callback(error);
            }
        } else {
            callback && callback(null, result);
        }
    });

};

module.exports = OTHERJSONRPCClient;